import axios from "axios";
import { HttpError } from "@/hooks/Error/HttpError";
import { ContentTypeEnum, RequestEnum } from "@/setting";
import qs from "qs";
import { cloneDeep, isFunction } from "lodash-es";

/**
 * transform 用于格式化数据
 */

export class VAxios {
  constructor(options) {
    this.options = options;
    this.axiosInstance = axios.create(options);

    this.setupInterceptors();
  }

  /** 拦截器配置 */
  setupInterceptors() {
    const {
      axiosInstance,
      options: { transform },
    } = this;
    if (!transform) return;

    const { requestInterceptors, responseInterceptors } = transform;

    const axiosCanceler = new AxiosCanceler();

    // 请求拦截器配置处理
    this.axiosInstance.interceptors.request.use(
      (config) => {
        // 成功请求
        const requestOptions =
          config.requestOptions || this.options.requestOptions; // 请求拦截器配置{url,method，baseURL,params,headers,timer，responseType}

        const ignoreCancelToken = requestOptions?.ignoreCancelToken ?? true; // 是否忽略请求取消的异常配置

        !ignoreCancelToken && axiosCanceler.addPending(config); // 添加请求到pending队列中
        return config;
      },
      (error) => {
        // 请求错误
        return Promise.reject(new HttpError("Http 请求发起异常", error));
      }
    );

    this.axiosInstance.interceptors.response.use(
      (response) => {
        if (response.status === 200) {
          const data = response.data;
          if (data.code === 200) {
            return data.data;
          } else {
            return Promise.reject(new HttpError(data.message, data));
          }
        } else {
          // 请求错误
          return Promise.reject(new HttpError("Http请求接收异常", response));
        }
      },
      (error) => {
        // 请求错误
        return Promise.reject(new HttpError("Http 请求接收异常", error));
      }
    );
  }

  /** 创建 请求实例 */
  createAxios(config) {
    this.axiosInstance = axios.create(config);
  }

  /**
   * 获取实例
   */
  getAxios() {
    return this.axiosInstance;
  }

  /** 重新配置axios */
  configAxios(config) {
    if (!this.axiosInstance) {
      return;
    }
    this.createAxios(config);
  }

  /**
   * 设置headers
   */
  setHeaders(headers) {
    if (!this.axiosInstance) {
      return;
    }
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }

  /**
   * 文件上传
   */
  uploadFile(config, params) {
    const formData = new window.FormData();
    const customFilename = params.name || "file";
    if (params.filename) {
      formData.append(customFilename, params.file, params, filename);
    } else {
      formData.append(customFilename, params.file);
    }

    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        const value = params.data[key];
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item);
          });
        } else {
          formData.append(key, params.data[key]);
        }
      });
    }

    return this.axiosInstance.request({
      ...config,
      method: "POST",
      data: formData,
      headers: {
        "Content-type": ContentTypeEnum.FORM_DATA,
        ignoreCancelToken: true,
      },
    });
  }

  supporFormData(config) {
    const headers = config.headers || this.options.headers;
    const contentType = headers["Content-Type"] || headers["content-type"];

    if (
      contentType !== ContentTypeEnum.FORM_URLENCODED ||
      !Reflect.has(config, "data") ||
      config.method.toUpperCase() === RequestEnum.GET
    ) {
      return config;
    }

    return {
      ...config,
      data: qs.stringify(config.data, { arrayFormat: "brackets" }),
    };
  }

  get(config, options) {
    return this.request({ ...config, method: RequestEnum.GET }, options);
  }

  post(config, options) {
    return this.request({ ...config, method: RequestEnum.POST }, options);
  }

  put(config, options) {
    return this.request({ ...config, method: RequestEnum.PUT }, options);
  }

  delete(config, options) {
    return this.request({ ...config, method: RequestEnum.DELETE }, options);
  }

  // 重新建立 request 结构,嵌入数据转换钩子
  request(config, options) {
    let conf = cloneDeep(config);
    // cancelToken 如果深拷贝，导致外层无法使用cancel取消请求
    if (config.cancelToken) {
      conf.cancelToken = config.cancelToken;
    }

    if (config.signal) {
      conf.signal = config.signal;
    }

    const opt = Object.assign({}, this.options, options);

    const { beforeRequestHook, requestCatchHook, transformResponseHook } =
      this.options.transform;

    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt);
    }

    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request(conf)
        .then((res) => {
          if (transformResponseHook && isFunction(transformResponseHook)) {
            try {
              const ret = transformResponseHook(res, opt);
              resolve(ret);
            } catch (err) {
              reject(HttpError("http request error", err));
            }
          } else {
            resolve(res);
          }
        })
        .catch((err) => {
          if (requestCatchHook && isFunction(requestCatchHook)) {
            reject(requestCatchHook(e, opt));
            return;
          }
          reject(e);
        });
    });
  }
}
